package cn.jofei.netty.hadler;

import cn.jofei.common.monitor.msg.DingMsg;
import cn.jofei.common.netty.handler.RelayHandler;
import cn.jofei.common.netty.message.Message;
import cn.jofei.common.netty.protocol.MessageCodec;
import cn.jofei.common.netty.protocol.ProtocolFrameDecoder;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.concurrent.DefaultPromise;
import io.netty.util.concurrent.Promise;
import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static cn.jofei.server.interf.ServerQueue.CLIENT_REG_INFO;
import static cn.jofei.server.interf.ServerQueue.REQUEST_QUEUE_NETTY;

/**
 * HttpProxyClientHandler
 *
 * @author Jofei Bao
 * @version ngbao v1.0.
 * @date 2022年10月17日 23:03
 */
@Data
public class TCPServerHandler extends ChannelInboundHandlerAdapter {

    private final Logger logger = LoggerFactory.getLogger(TCPServerHandler.class);
    private Integer port;

    public TCPServerHandler(Integer port) {
        this.port = port;
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        super.channelActive(ctx);

        // 停止当前通道的读取，直到客户端远程通道建立
        ctx.channel().config().setAutoRead(false);

        Message message = Message.notify(port);
        Promise<Channel> promise = new DefaultPromise<>(ctx.channel().eventLoop());
        //添加到 sequenceId -> promise 映射
        REQUEST_QUEUE_NETTY.putIfAbsent(message.getSequenceId(), promise);
        // 拿到客户端 通信通道
        ChannelHandlerContext context = CLIENT_REG_INFO.get(port + "");
        if (context != null) {
            promise.addListener(e -> {
                if (e.isSuccess()) {
                    // 客户端的 数据通道
                    Channel cChannel = (Channel) e.get();

                    cChannel.pipeline().remove(LoggingHandler.class);
                    cChannel.pipeline().remove(ProtocolFrameDecoder.class);
                    cChannel.pipeline().remove(IdleStateHandler.class);
                    cChannel.pipeline().remove(MessageCodec.class);
                    cChannel.pipeline().remove(ClientMessageHandler.class);

//                cChannel.writeAndFlush(msg);

                    /**
                     * 第一个完整Http请求处理完毕后，不需要解析任何 Http 数据了，直接盲目转发 TCP 流就行了
                     * 所以无论是连接客户端的 clientChannel 还是连接远端主机的 remoteChannel 都只需要一个 RelayHandler 就行了。
                     * 代理服务器在中间做转发。
                     *
                     * 客户端   --->  clientChannel --->  代理 ---> remoteChannel ---> 远端主机
                     * 远端主机 --->  remoteChannel  --->  代理 ---> clientChannel ---> 客户端
                     */
                    ctx.pipeline().remove(TCPServerHandler.this);
                    ctx.pipeline().addLast(new RelayHandler(cChannel));

                    cChannel.pipeline().addLast(new RelayHandler(ctx.channel()));

                    // 拿到客户端远程通道，继续读取
                    ctx.channel().config().setAutoRead(true);
                    cChannel.config().setAutoRead(true);

                    // 发起通道搭建完成的通知
                    context.writeAndFlush(message.established());
                } else {
                    ctx.close();
                }
            });
            // 发起通知
            context.writeAndFlush(message);
        } else {
            ctx.close();
        }


    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        flushAndClose(ctx.channel());
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        DingMsg.markdown("Ngbao监控").text(String.format("#### Ngbao监控\n#### TCP服务异常\n##### 原因：%s", cause.getMessage())).build().send();
        flushAndClose(ctx.channel());
    }

    private void flushAndClose(Channel ch) {
        if (ch != null && ch.isActive()) {
//            ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
            ch.close();
        }
    }
}
